package clusteringAlg;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.opencv.core.Mat;
import org.opencv.core.Size;


public class KMeans extends SamplesSelector{

	List<Mean> means = new ArrayList<Mean>();
	ProcessImage stylePI;
	
	/**
	 * @return the stylePI
	 */
	public ProcessImage getStylePI() {
		return stylePI;
	}

	/**
	 * @param stylePI the stylePI to set
	 */
	public void setStylePI(ProcessImage stylePI) {
		stylePI = stylePI;
	}
	
	public KMeans() {
		super();
		// TODO Auto-generated constructor stub
	}

	/**
	 * @param person
	 * @param nClusters
	 * @param iterations
	 */
	public KMeans(Person person, int nClusters, int iterations) {
		super(person, nClusters, iterations);
		// TODO Auto-generated constructor stub
	}

	/**
	 * @param person
	 * @param nClusters
	 * @param iterations
	 * @param stylePI
	 */
	public KMeans(Person person, int nClusters, int iterations,
			ProcessImage stylePI) {
		super(person, nClusters, iterations);
		this.stylePI = stylePI;
	}

	private void initAlg(){
		
		Random r = new Random();
		int n=0;		
		
		// vytvorenie neuronov s nahodnymi pociatocnymi hodnotami
		for(int i=0; i < this.nClusters; i++){
		
			n=r.nextInt(this.person.GetSampleCount()-1);
			Mat m = person.getSample(n).getImage().clone();
			
			Mean mean = new Mean(m);
			
			switch(this.getStylePI()){
			
			case GAUSS_BLUR:
				mean.BlurImage(new Size(r.nextInt(6)+6, r.nextInt(6)+6));	
				break;
			case GAUSS_NOISE:
				mean.GaussianNoiseImage(0, 0.01);
				break;
			default:
				break;
			
			}
			
			mean.ScaleImage(0, 1);
			
			this.means.add(mean);
			
		}
				
		this.ScaleData(0, 1);
	}
	
	private void KmeansAlg(){
		
		//Sample sample;
		int counter = 0;
		double min;
		double dist=0;
		int minIndex = -1;
		
		//Help h = new Help(new Size(64,64));
		
		this.initAlg();
		//h.saveImages("D:/skola/diplomovka/vysledky/vstup", this.GetMeanSamples());
				
		for (int i=0; i<this.iterations; i++){
				
			MeansClear();
			
			for (Sample sample: this.person.getSamples()){
				
				min = Double.MAX_VALUE;
				dist = 0;
				minIndex = -1;
				counter = 0;
				
				for (Mean mean: this.means){
					dist = EukclideanDistance(sample.getScaledImage(), mean.getScaledImage());
					if (dist < min){
						min = dist;
						minIndex = counter;
					}
					counter++;
				}
				
				RelationSample rSample = new RelationSample(sample, 1);
				
				this.means.get(minIndex).addImages(rSample);
			}

			double d = MeansUpdate();
			//System.out.println(i + ", " + d);
			if (d == 0)
				break;
			
			//h.saveImages("D:/skola/diplomovka/vysledky/folder", i, this.GetMeanSamples());
			
		}
		
		for (Mean m : this.means){
			m.SetImageFromScaledImage(0, 255);
		}
	}
	
	public void Run(){
		
		KmeansAlg();
		
	}

	private void MeansClear() {
		for (Mean mean: this.means){
			mean.setImages(new ArrayList<RelationSample>());
		}
		
	}

	private double MeansUpdate() {
		double var = 0;
		for (Mean mean: this.means){

			var = mean.CalcMean();
			//mean.SetImageFromScaledImage(0, 255);
		}
		return var;
	}

	/**
	 * @return the means
	 */
	public List<Mean> getMeans() {
		return means;
	}

	/**
	 * @param means the means to set
	 */
	public void setMeans(List<Mean> means) {
		this.means = means;
	}

	@Override
	public List<Sample> GetMeanSamples() {

		List<Sample> samples = new ArrayList<Sample>();
		
		for (Sample m: this.getMeans()){
			samples.add(m);
		}
		
		return samples;
	}
	
	@Override
	public List<Sample> GetClosestSamples() {

		List<Sample> samples = this.GetMeanSamples();
		double min, dst;
		int counter = 0;
		
		for (Sample n: this.GetMeanSamples()){
		
			min=Double.MAX_VALUE;
			
			for (Sample m: this.person.getSamples()){
				dst = this.EukclideanDistance(m.getScaledImage(), n.getScaledImage());
				
				if (dst < min){
					samples.set(counter, m);
					min = dst;
				}
			}
			counter++;
		}
		
		return samples;
	}
	
}


